CodeBuildでBatch用イメージと同時にジョブ定義を作ってみた
こんちにちは。AWS事業本部のKyoです。
AWS Batch用のコンテナイメージのビルドと同時にジョブ定義も作られたら便利だよね、をやってみました。
パイプラインの構築は出来ているものとして、主にbuildspec.ymlやBatchのジョブ定義の設定JSON(以下、ジョブ定義JSON)などを紹介します。また、ハマったところについても触れています。
3行まとめ
- CodeCommitにDockerfile, buildspec.yml に加えてジョブ定義JSONを格納
- CodeBuildの中でjqを使ってビルドしたイメージの名前とタグをジョブ定義JSONに反映
- イメージをECRにプッシュ後、AWS CLIを使ってジョブ定義JSONをBatchへ登録
やってみる
CodeCommit
以下のファイルを格納します。
- Dockerfile
- job-definition-template.json
- buildspec.yml
それぞれについて詳しく見ていきます。
Dockerfile
ビルドするイメージのDockerfileです。特別な対応は不要でビルドが通るものであればなんでも良いです。
今回はAmazon Linuxのイメージを利用します。
FROM amazonlinux:latest
job-definition-template.json
Batchのジョブ定義の設定値が含まれるJSONです。
CodeCommitに格納するものは、イメージ名とタグが入っていない状態なのでjob-definition-template.json
という名前にしてあります。ビルドの途中でjob-definition.json
を生成します。
AWS CLIからregister-job-definitionコマンドで利用します。
- imageの値はビルドしたイメージ名とタグで上書きされるので任意の値でかまいません
- ハイライトしたjobRoleArnはジョブで利用するロールのARNに書き換えてください
- このJSONの設定はEC2起動タイプ用です
{ "image": "To Be Determined", "vcpus": 1, "memory": 2048, "command": [ "echo 'hello world'" ], "jobRoleArn": "【your jobRoleArn】", "executionRoleArn": "", "volumes": [ { "host": { "sourcePath": "" }, "name": "" } ], "environment": [ ], "mountPoints": [], "readonlyRootFilesystem": true, "privileged": true, "ulimits": [ { "hardLimit": 0, "name": "", "softLimit": 0 } ], "user": "", "resourceRequirements": [], "linuxParameters": { "devices": [], "tmpfs": [], "maxSwap": 0, "swappiness": 0 }, "secrets": [] }
buildspec.yml
オーソドックスなコンテナイメージのビルドに加えて以下のことを行っています。
- JSONLintで
job-definition-template.json
のフォーマットチェック - jqでビルドしたイメージ名とタグを
job-definition-template.json
へ反映 - AWS CLIでBatchへのジョブ登録
version: 0.2 phases: install: commands: # JSONLintをインストール - npm install jsonlint -g # AWS CLI v2を最新バージョンで利用するため再インストール - pip3 uninstall awscli -y - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" - unzip awscliv2.zip - ./aws/install -i /usr/local/aws-cli -b /usr/local/bin pre_build: commands: # ジョブ定義テンプレートのフォーマットをチェック - jsonlint -q job-definition-template.json - IMAGE_TAG=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | head -c 7) - echo Logging in to Amazon ECR... - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com build: commands: - echo Build started on `date` - echo Building the Docker image... - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Build completed on `date` - echo Pushing the Docker image... - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG # 今回ビルドしたイメージ名とタグをジョブ定義テンプレートに反映し、Batchのジョブ定義として登録 - cat job-definition-template.json | jq --arg IMAGE_URI $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG '.image|=$IMAGE_URI' > job-definition.json - aws batch register-job-definition --job-definition-name $JOB_DEFINITION_NAME --type container --container-properties file://job-definition.json
CodeBuild
環境変数
buildspec.yml
で利用しているので以下をCodeBuildに環境変数として持たせてください。
- AWS_DEFAULT_REGION : 利用するリージョン
- AWS_ACCOUNT_ID : 利用するAWSアカウントの12桁ID
- IMAGE_REPO_NAME : 利用するECRのレポジトリ名
- JOB_DEFINITION_NAME : ジョブ定義を登録する際の名称。今回は
sample-al
IAMロール
CodeBuildのサービスロールです。
公式ドキュメントを参考にポリシーとロールを作成しました。 ポリシーは以下の通りで、Batchへの対応としてハイライト部分の権限を追加しています。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject", "s3:PutObject", "s3:GetObjectVersion", "s3:GetBucketAcl", "s3:GetBucketLocation" ], "Resource": "*", "Effect": "Allow" }, { "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*", "Effect": "Allow" }, { "Action": [ "codecommit:GitPull" ], "Resource": "*", "Effect": "Allow" }, { "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:CompleteLayerUpload", "ecr:GetAuthorizationToken", "ecr:InitiateLayerUpload", "ecr:PutImage", "ecr:UploadLayerPart" ], "Resource": "*", "Effect": "Allow" }, { "Action": [ "batch:RegisterJobDefinition" ], "Resource": "*", "Effect": "Allow" }, { "Condition": { "StringEqualsIfExists": { "iam:PassedToService": [ "ecs-tasks.amazonaws.com" ] } }, "Action": [ "iam:PassRole" ], "Resource": "*", "Effect": "Allow" } ] }
PassRoleを見落としていると、ジョブ登録時に以下のエラーが発生します。
An error occurred (AccessDeniedException) when calling the RegisterJobDefinition operation: User: arn:aws:sts::XXXXXXXXXXXX:assumed-role/CodeBuildServiceRole/AWSCodeBuild-5abf6672-7cda-444b-8faa-c7dd957767de is not authorized to perform: iam:PassRole on resource: arn:aws:iam::XXXXXXXXXXXX:role/BatchJobRole
ビルドの実行
CodeCommitをソースにCodeBuildでビルドを実行します。
出力例
###### 前略 ###### [Container] 2021/03/30 15:10:12 Running command docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG The push refers to repository [XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/sample-al] e84e86a2ce7e: Preparing e84e86a2ce7e: Pushed 2c158eb: digest: sha256:b288803ecb4fe526981317f7574cc87160132f635300eb3325859e9cb4a75eb9 size: 529 [Container] 2021/03/30 15:10:21 Running command cat job-definition-template.json | jq --arg IMAGE_URI $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG '.image|=$IMAGE_URI' > job-definition.json [Container] 2021/03/30 15:10:22 Running command aws batch register-job-definition --job-definition-name $CONTAINER_NAME --type container --container-properties file://job-definition.json { "jobDefinitionName": "sample-al", "jobDefinitionArn": "arn:aws:batch:ap-northeast-1:XXXXXXXXXXXX:job-definition/sample-al:1", "revision": 1 } [Container] 2021/03/30 15:10:23 Phase complete: POST_BUILD State: SUCCEEDED [Container] 2021/03/30 15:10:23 Phase context status code: Message:
ECRにイメージが格納され、Batchのジョブ定義が作成(or リビジョンが作成)されていることが確認できれば完了です。
ハマったところ
AWS CLIからのregister-job-definition
では、ジョブ定義JSONを参照する形になりますが、これに必要なパラメータを探るのに苦労しました。
Batchの実体はECSのため、以下のブログの「タスク定義」を「ジョブ定義」に読み替えるとイメージが伝わると思います。
「そんなん、既存の登録内容からその内容をJSONで出力してそれをそのまま使えばええやん?」と思いますよね。はい。そんなAPIはありません。
最初の1回はとにかくエラーを潰す泥臭い作業が必要になりますが、作ってしまえばコードとして管理できるので変更や再利用は行いやすいです。
やってしまった系のミスとしては、メモリを1桁多く設定し、「Batchインスタンスがいつまでたっても起動してこない。。」みたいなこともありました。
AWS Batch ジョブが RUNNABLE ステータスで止まっているのはなぜですか?
このように試行錯誤していると、DockerHubのダウンロード制限にも結構な頻度でひっかかったりもしました。ビルドが失敗した際にログを見るまで、設定のせいなのか、制限のせいか分からず、割とモヤモヤが募るので、検証する前にぜひ以下の設定をしておきましょう。
おわりに
本ブログではコンテナイメージをビルドする度にジョブ定義も作成される設定を紹介しました。またこの設定を煮詰めていく中で出会ったハマりどころについても紹介しました。
以上、何かのお役に立てれば幸いです。